# -*- coding: utf-8 -*-

from flask import Blueprint, render_template, current_app, send_from_directory, request, jsonify
from xml.dom.minidom import parseString
import signal
import subprocess
import os
import time
import json
import dicttoxml
import xmltodict
import xml.etree.ElementTree as ET
from xml.dom import minidom

mappingProcess = -1
navgationProcess = -1

home = Blueprint('home', __name__)

class DocumentReader:
    def __init__(self, real_path):
        self.real_path = real_path

    def analysis_dir(self):
        dirs = []
        files = []
        os.chdir(self.real_path)    #os.chdir()方法用于改变当前工作目录到指定的路径
        for name in sorted(os.listdir('.'), key=lambda x: x.lower()):
            _time = time.strftime("%Y/%m/%d %H:%M", time.localtime(os.path.getctime(name)))
            if os.path.isdir(name):
                dirs.append([name, _time, '文件夹', '-'])
            elif os.path.isfile(name):
                file_type = os.path.splitext(name)[1]
                size = self.get_size(os.path.getsize(name))
                files.append([name, _time, file_type, size])
        return dirs, files

    @staticmethod
    def get_size(size):
        if size < 1024:
            return '%d  B' % size
        elif 1024 <= size < 1024 * 1024:
            return '%.2f KB' % (size / 1024)
        elif 1024 * 1024 <= size < 1024 * 1024 * 1024:
            return '%.2f MB' % (size / (1024 * 1024))
        else:
            return '%.2f GB' % (size / (1024 * 1024 * 1024))

class roslaunch_process():
    @classmethod
    def start_navigation(self):
        node = current_app.config['NAVIGATE_NODE'];
        cmd = current_app.config['NAV_CMD'];
        self.process_navigation = subprocess.Popen(["roslaunch", "--wait", node, cmd])

    @classmethod
    def stop_navigation(self):
        self.process_navigation.send_signal(signal.SIGINT)	

    @classmethod
    def start_mapping(self):
        node = current_app.config['MAPPING_NODE'];  #通过current_app获取参数，需要导入current_app
        cmd = current_app.config['MAPPING_CMD'];
        self.process_mapping = subprocess.Popen(["roslaunch", "--wait", node, cmd])
        
    @classmethod
    def stop_mapping(self):
        self.process_mapping.send_signal(signal.SIGINT) 

class MapParser:
    # 初始化静态成员
    points = {}
    paths = []
 
    @classmethod
    def parse_map(cls, xml_content):
        # 清空静态成员，以便每次解析时都是新的开始
        cls.points.clear()
        cls.paths.clear()
 
        root = ET.fromstring(xml_content)
 
        # 解析点
        for point in root.findall(".//point"):
            point_id = int(point.get('id'))
            x = float(point.get('x'))
            y = float(point.get('y'))
            cls.points[point_id] = (x, y)
 
        # 解析路径
        for path in root.findall(".//path"):
            start = int(path.get('startPointId'))
            end = int(path.get('endPointId'))
            line_velocity = float(path.get('lineVelocity'))
            cls.paths.append((start, end, line_velocity))

def convert_to_xml(data):
    model = ET.Element('model')
    header = ET.SubElement(model, 'header', name='map', mapVersion='1.0.0')
    group = ET.SubElement(model, 'group', mapId='965680015')
    ET.SubElement(group, 'agv_number', id='1')
    map_element = ET.SubElement(model, 'map', id='965680015')

    point_ids = set()

    # 添加点和路径到 XML
    for path in data['PATH']:
        # 添加起点
        start_point = path['startPoint']
        if start_point['num'] not in point_ids:
            point = ET.SubElement(map_element, 'point', 
                                  id=str(start_point['num']),
                                  x=str(start_point['x']),
                                  y=str(start_point['y']),
                                  angle=str(start_point['rotation']),
                                  type='0',
                                  qrCodeId='-1',
                                  qrAngle='1.5707963267948966',
                                  maxSpinVel='3.4',
                                  spinAccVel='1.75',
                                  spinDccVel='1.75',
                                  avoidType='-1',
                                  enableSpin='1',
                                  enableNodeAngle='0',
                                  enableLoadCentering='0',
                                  enableTableTunning='0',
                                  bits='0',
                                  qrOffsetX='0.0',
                                  qrOffsetY='0.0',
                                  qrOffsetAngle='0.0',
                                  word1='-1',
                                  word2='-1',
                                  headPick='0.0',
                                  headDrop='0.0',
                                  turtleVelocity='0.1',
                                  turtleDis='0.3',
                                  maxSpinVelLoad='1.0',
                                  spinAccVelLoad='0.6',
                                  spinDccVelLoad='0.6')
            point_ids.add(start_point['num'])
            print("add point:", start_point['num'])

        # 添加终点
        end_point = path['endPoint']
        if end_point['num'] not in point_ids:
            point = ET.SubElement(map_element, 'point', 
                                  id=str(end_point['num']),
                                  x=str(end_point['x']),
                                  y=str(end_point['y']),
                                  angle=str(end_point['rotation']),
                                  type='0',
                                  qrCodeId='-1',
                                  qrAngle='1.5707963267948966',
                                  maxSpinVel='3.4',
                                  spinAccVel='1.75',
                                  spinDccVel='1.75',
                                  avoidType='-1',
                                  enableSpin='1',
                                  enableNodeAngle='0',
                                  enableLoadCentering='0',
                                  enableTableTunning='0',
                                  bits='0',
                                  qrOffsetX='0.0',
                                  qrOffsetY='0.0',
                                  qrOffsetAngle='0.0',
                                  word1='-1',
                                  word2='-1',
                                  headPick='0.0',
                                  headDrop='0.0',
                                  turtleVelocity='0.1',
                                  turtleDis='0.3',
                                  maxSpinVelLoad='1.0',
                                  spinAccVelLoad='0.6',
                                  spinDccVelLoad='0.6')
            point_ids.add(end_point['num'])
            print("add point:", end_point['num'])
            
    for path in data['PATH']:
        start_point = path['startPoint']
        end_point = path['endPoint']
        cp1 = path['cp1']
        cp2 = path['cp2']
        print("path:", path)
        # 添加路径
        if path['mode'] == 'line':
            path_element = ET.SubElement(map_element, 'path', 
                                         id=str(path['index']),
                                         type='LinePath',
                                         direction=path['segment'],
                                         startPointId=str(start_point['num']),
                                         endPointId=str(end_point['num']),
                                         lineVelocity=str(path['velocity']),
                                         loadLineVelocity='1',
                                         avoidType='-1',
                                         guideType='laser',
                                         maxDeadDist='0.3',
                                         stopDisEmpty='0.0',
                                         stopDistFull='0.0',
                                         control1PosX='0.0',
                                         control1PosY='0.0',
                                         control2PosX='0.0',
                                         control2PosY='0.0',
                                         angHead='0.0',
                                         sideMove='0',
                                         bits='0',
                                         word1='-1',
                                         word2='-1',
                                         rfid='-1',
                                         rfidDisOffset='-1.0',
                                         magDisOffset='-1.0',
                                         blindWalkingAllow='0',
                                         markIgnore='0')
        elif path['mode'] == 'bezier':
            path_element = ET.SubElement(map_element, 'path', 
                                         id=str(path['index']),
                                         type='BezierPath',
                                         direction=path['segment'],
                                         startPointId=str(start_point['num']),
                                         endPointId=str(end_point['num']),
                                         lineVelocity=str(path['velocity']),
                                         loadLineVelocity='1',
                                         avoidType='-1',
                                         guideType='laser',
                                         maxDeadDist='0.3',
                                         stopDisEmpty='0.0',
                                         stopDistFull='0.0',
                                         control1PosX=str(cp1['x']),
                                         control1PosY=str(cp1['y']),
                                         control2PosX=str(cp2['x']),
                                         control2PosY=str(cp2['y']),
                                         angHead='0.0',
                                         sideMove='0',
                                         bits='0',
                                         word1='-1',
                                         word2='-1',
                                         rfid='-1',
                                         rfidDisOffset='-1.0',
                                         magDisOffset='-1.0',
                                         blindWalkingAllow='0',
                                         markIgnore='0')
        print("add path:", path['index'])
    return model  

@home.route('/index')
@home.route('/index/<path:path_uri>')
def index(path_uri=''):
    # 开始导航
    # navigate()
    # 
    base_dir = current_app.config['BASEDIR']
    real_path = os.path.join(base_dir, path_uri).replace('\\', '/')     #os.path.join()函数用于路径拼接文件路径，可以传入多个路径
    # print("real_path", real_path)
    if not os.path.exists(real_path):
        return render_template('index.html', path=None, dirs='', files='', error_info="wrong path...")     #render_template()函数是flask函数，它从模版文件夹templates中呈现给定的模板上下文。
    file_reader = DocumentReader(real_path)
    dirs, files = file_reader.analysis_dir()
    return render_template('index.html', path=path_uri, dirs=dirs, files=files, error_info=None)

@home.route('/download/<filename>')
@home.route('/download/<path:path>/<filename>')
def download(filename, path=None):
    if not path:
        real_path = current_app.config['BASEDIR']
    else:
        real_path = os.path.join(current_app.config['BASEDIR'], path)
    return send_from_directory(real_path, filename, mimetype='application/octet-stream')

@home.route('/upload', methods=['GET', 'POST'])
def upload():
    if request.method == 'GET':
        return "is upload file ... "
    else:
        path = current_app.config['PARAMDIR']
        file = request.files['file']
        file_name = file.filename
        file.save(os.path.join(path, file_name))
        return jsonify({"code": 200, "info": "文件：%s 上传成功" % file_name})

@home.route('/navigate')
def navigate():
    global mappingProcess
    global navgationProcess
    # stop mapping process
    if mappingProcess > 0:
        roslaunch_process.stop_mapping()
        mappingProcess = -1
        time.sleep(2)
    # start navation process
    if navgationProcess < 0:
        roslaunch_process.start_navigation()
        if roslaunch_process.process_navigation.returncode == None :    #returncode=0 表示执行成功
           navgationProcess = roslaunch_process.process_navigation.pid
        else:
            print ("navgtion process error!")    
   
@home.route('/mapping')
def mapping():
    global mappingProcess
    global navgationProcess
    # stop navgation process
    if navgationProcess > 0:
        roslaunch_process.stop_navigation()
        navgationProcess = -1
        time.sleep(2)
    # start mapping process
    if mappingProcess < 0 :
        roslaunch_process.start_mapping()
        if roslaunch_process.process_mapping.returncode == None :
            mappingProcess = roslaunch_process.process_mapping.pid
        else:
            print ("mapping process error!")
    else:
        print ("map process ", mappingProcess, "already start!")
    return render_template('mapping.html')

@home.route('/load_path', methods=['GET', 'POST'])
def load_path():
    if request.method == 'GET':
       map_dir = current_app.config['MAPDIR']
       fn = request.args.get('name')
       with open(map_dir + fn, "r") as fin:
            lines = fin.readlines()
            lines = ''.join(str(i) for i in lines)
            MapParser.parse_map(lines)
            xpars = xmltodict.parse(lines, process_namespaces=False, encoding='utf-8')
            xpars = json.dumps(xpars)
            # print (xpars)
       return jsonify(xpars)
   
@home.route('/upload_path', methods=['POST'], strict_slashes=False)
def upload_path():
    if request.method == "POST":
        data = request.get_json()
        print("data:", data)
        xml_data = convert_to_xml(data)
        # print("xml_data:", xml_data)
        if request.get_json()["PATH"]:
            map_dir = current_app.config['MAPDIR']
            # xml = dicttoxml.dicttoxml(xml_data, custom_root='model')
            xml_str = ET.tostring(xml_data, encoding='utf-8').decode('utf-8')
            formatted_xml = minidom.parseString(xml_str).toprettyxml(indent="  ")
            print(formatted_xml)
            with open(map_dir + "mapTest.xml", "w") as f:
                f.write(formatted_xml)
            return jsonify({"code": 200, "info": "Success!"})
        else :
            return jsonify({"code": 400, "info": "Failed!"})

@home.errorhandler(500)
def error(error):
    return render_template('index.html', error_info="error path...")

